package com.zf.ognl;

import lombok.extern.slf4j.Slf4j;
import ognl.Ognl;
import ognl.OgnlException;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Ognl表达式测试
 */
@Slf4j
public class OgnlTest {

    public static void main(String[] args) throws OgnlException {
        Map<String, Object> context = new HashMap<>();
        context.put("id", "1");
        // context.put("id", "ZZ");
        context.put("name", "XXX");
        context.put("score", "ZZ");
        context.put("sex", "man");
        context.put("biggest", true);
        context.put("countryCodeArray", new String[]{"US", "CA", "IN"});
        context.put("countryCodeList", Arrays.asList("US", "CA", "IN"));
        HashMap<String, Object> stu = new HashMap<>();
        stu.put("name", "张三");
        stu.put("age", 14);
        context.put("stu", stu);
        context.put("stu2", new Student("李四", 21));
        System.out.println(context);


        System.out.println("单个字符的数字型字符串：");
        // 单个字符字符串注意事项：
        //      1、当使用 单引号修饰单字符字符串时，字符串会被识别成 Character 字符，Ognl会将传参 "id" 转为Double（如果不能转则抛出NumberFormatException异常），
        //              同时再将Character字符转为Double(得到字符的ASCII码值)，然后两者再比较
        //      2、使用双引号修饰单字符字符串 或者 调用'单字符字符串'.toString() 方法 后，字符串被识别成 String类型，Ognl将传参"id"转为字符串后再比较
        System.out.println(getValue("id == '1'", context));// false
        System.out.println(getValue("id == \"1\"", context));// true
        System.out.println(getValue("id == 1", context));// true
        System.out.println(getValue("id == '1'.toString()", context));// true

        System.out.println("多个字符的纯数字型字符串：");
        // 多个数字字符串注意事项：
        //      1、当没有修饰符时，把常量值识别为数字类型，会把传传参 "score"  转为Double（如果不能转则抛出NumberFormatException异常），然后和常量值比较
        //      2、当有 单引号或者双引号 修饰符时，都转为字符串后再比较
        System.out.println(getValue("score == '88'", context));// false
        System.out.println(getValue("score == \"88\"", context));// false
        // System.out.println(getValue("score == 88", context));// 报错 "ZZ"不能转为数字
        System.out.println(getValue("score == '88'.toString()", context));// false


        System.out.println("单个字符的非数字型字符串：");
        // 和 "单个字符的数字型字符串"注意事项情况一致
        // Mybatis的标签判断条件中要注意这种情况
        try {
            System.out.println(getValue("name == 'z'", context)); // 报错
        } catch (Exception e) {
            // 'XXX'不能被转成数值型，此处会抛出NumberFormatException
            log.error("错误：", e);
        }
        System.out.println(getValue("name == ''", context)); // 空字符串不会报错
        // 使用双引号，或者 调用.toString() 方法，可以解决上述问题
        System.out.println(getValue("name == \"z\"", context));// false
        System.out.println(getValue("name == 'z'.toString()", context));// false


        System.out.println("多个字符的非纯数字字符串：");
        // 不是单个字符的字符串，单引号和双引号没有区别
        System.out.println(getValue("sex == 'man'", context));// true
        System.out.println(getValue("sex == \"man\"", context));// true
        System.out.println(getValue("sex == 'man'.toString()", context));// true


        System.out.println("boolean值判断：");
        System.out.println(getValue("biggest==true", context)); // true
        System.out.println(getValue("biggest==false", context)); // false


        System.out.println("数组操作：");
        System.out.println(getValue("countryCodeArray.length", context)); // 长度
        // System.out.println(getValue("countryCodeArray.size", context)); // 报错，没有size属性
        System.out.println(getValue("countryCodeArray[0]", context)); // 取第一个


        System.out.println("集合操作：");
        System.out.println(getValue("countryCodeList.size", context)); // 长度
        // System.out.println(getValue("countryCodeList.length", context)); // 报错，没有 length属性
        System.out.println(getValue("countryCodeList[0]", context)); // 取第一个
        System.out.println(getValue("countryCodeList.get(0)", context)); // 取第一个
        System.out.println(getValue("countryCodeList.isEmpty", context)); // 判空
        System.out.println(getValue("countryCodeList.isEmpty==true", context)); // 判空
        System.out.println(getValue("countryCodeList.isEmpty()", context)); // 判空
        System.out.println(getValue("countryCodeList.isEmpty()==true", context)); // 判空


        System.out.println("静态属性：");
        System.out.println(getValue("@com.zf.ognl.AnimalEnum@CAT", context));
        System.out.println(getValue("@com.zf.ognl.AnimalEnum@CAT.getAge", context));
        System.out.println(getValue("@com.zf.ognl.AnimalEnum@CAT.getAge()", context));
        System.out.println(getValue("@com.zf.ognl.AnimalEnum@CAT.age", context)); // 还是调用 getAge()
        // System.out.println(getValue("@com.zf.ognl.AnimalEnum@CAT.getXxx()", context)); // 报错，没有 getXxx 方法
        // System.out.println(getValue("@com.zf.ognl.AnimalEnum@CAT.xxx()", context)); // 报错，没有 xxx 方法

        System.out.println("静态方法调用：");
        System.out.println(getValue("@java.lang.Integer@parseInt('-77')", context));
        System.out.println(getValue("@java.lang.Math@min(22, 33)", context));
        System.out.println(getValue("@@min(44, 55)", context)); // java.lang.Math中的静态方法可以简化为 '@@'

        System.out.println("非静态属性 Map类型数据：");
        System.out.println(getValue("stu.name", context));
        System.out.println(getValue("stu.getName", context)); // 返回 null, 没有 getName key
        System.out.println(getValue("stu.age", context));

        System.out.println("非静态属性 实体类型数据：");
        System.out.println(getValue("stu2.name", context)); // 调用 getName() 方法
        System.out.println(getValue("stu2.getName", context));
        System.out.println(getValue("stu2.getName()", context));
        System.out.println(getValue("stu2.age", context)); // 调用 getAge() 方法
        // System.out.println(getValue("stu2.xxx", context)); // 报错，没有 xxx 属性
        // System.out.println(getValue("stu2.getXxx", context)); // 报错，没有 getXxx 属性

    }

    public static Object getValue(String expression, Object context) throws OgnlException {
        return Ognl.getValue(expression, context);
    }
}
